package com.two.screen.water.balance.serial.weight

import com.cyItem.sesrialPort.SerialPort
import com.cyItem.sesrialPort.SerialPortFinder
import com.two.screen.water.balance.bean.WeightBean
import com.two.screen.water.balance.net.api.DataApi
import com.two.screen.water.balance.utils.*
import com.two.screen.water.balance.utils.UtilsBigDecimal.toDoubleWithTwoDecimalPlaces
import com.two.screen.water.balance.utils.eventbus.Event
import com.two.screen.water.balance.utils.eventbus.EventBusUtil
import com.two.screen.water.balance.utils.eventbus.EventCode
import java.io.File
import java.io.InputStream
import kotlin.experimental.xor

object SerialPortWeightZzUtils {
    private var mSerialPort: SerialPort? = null
    private var mInputStream: InputStream? = null
    private var mReadThread: Thread? = null
    private var mDevicePathArray: Array<String>? = null
    private var mDevicePath: String? = null
    private var mSerialPortPath = DataApi.mWeightSerialPortPath ?: "/dev/ttyS9"
    private var mSerialPortPort = 9600
    private var mIsStable = false //是否稳定
    private var mTempWeight = 0.0//用来做去皮 和清皮统计。

    //超赢一体秤的默认端口为 /dev/ttyS4，故默认选中该端口
    fun initSerialPortWeight() {
        close()
        mSerialPortPath = DataApi.mWeightSerialPortPath ?: "/dev/ttyS9"
        mDevicePathArray = SerialPortFinder().allDevicesPath

        if (mDevicePathArray == null || mDevicePathArray!!.isEmpty()) {
            return
        }
        mDevicePathArray?.forEach {
            if (mSerialPortPath == it) {
                mDevicePath = it
            }
            d("串口：$it")
        }
        d("串口：确认：$mDevicePath")
        if (mDevicePath.isNullOrBlank()) {
            return
        }

        mSerialPort = try {
            SerialPort(File(mSerialPortPath), mSerialPortPort, 0)
        } catch (e: Exception) {
            d("串口通信失败，请重试！")
            closeSerialPort()
            return
        }

        mInputStream = mSerialPort?.inputStream
        if (mInputStream == null) {
            d("电子秤端口或者波特率未正确配置！")
            closeSerialPort()
            return
        }
        DataApi.mSerialPortPortWeight = mSerialPortPort
        DataApi.mSerialPortPathWeight = mSerialPortPath
        SPUtils.putString(Params.mSerialPortPath, mSerialPortPath)
        SPUtils.putInt(Params.mSerialPortPort, mSerialPortPort)
        mReadThread = ReadThread()
        mReadThread!!.start()
    }

    fun close() {
        closeThreadQuiet()
        closeSerialPort()
    }

    //关闭线程
    private fun closeThreadQuiet() {
        if (mReadThread != null) {
            try {
                mReadThread?.interrupt()
            } catch (e: java.lang.Exception) {
                // just ignore
            } finally {
                mReadThread = null
            }
        }
    }

    //关闭串口
    private fun closeSerialPort() {
        if (mSerialPort != null) {
            try {
                if (mInputStream != null) {
                    mInputStream!!.close()
                }
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
            if (mSerialPort!!.isOpen) {
                mSerialPort!!.closePort()
            }
            mSerialPort = null
        }
    }


    //每一包数据最大长度
    private val mZzDataMaxSize = 50

    //缓存区长度
    private val mZzBuffSize = mZzDataMaxSize * 2

    //缓存区
    private val mZzBuff = ByteArray(mZzBuffSize)

    //缓存区可存入的下一个数据下标（即已存入的数据长度）
    private var mZzBuffIndex = 0

    //头
    private val mZzHead = byteArrayOf(0X4e.toByte(), 0X54.toByte())

    //输出
    private var mZzOutBuff: ByteArray? = null

    //每条数据长度
    private var mZzDataLength = 29

    internal class ReadThread : Thread() {
        override fun run() {
            // 读取数组
            val bytes = ByteArray(29)
            while (true) {
                try {
                    if (isInterrupted) break
                    val len = mInputStream!!.read(bytes)
                    if (len < 12) continue
                    //TODO:这里可以处理指令
                    if (0x02.toByte() == bytes[0] && 0x00.toByte() == bytes[1] && 0x01.toByte() == bytes[2] && 0x00.toByte() == bytes[3] && 0x03.toByte() == bytes[4]) {
                        //去皮、置零成功
                    }
                    d("处理前：${ByteUtils.bytes2HexStr(bytes)}")
                    bytes.forEach { oneByte ->
                        if (mZzBuffIndex >= mZzBuff.size) {
                            //缓存区已满，删除第一个
                            if (mZzBuff.size - 1 >= 0) {
                                System.arraycopy(mZzBuff, 1, mZzBuff, 0, mZzBuff.size - 1)
                                mZzBuff[mZzBuff.size - 1] = 0X00
                                mZzBuffIndex = mZzBuff.size - 1
                            }
                        }
                        mZzBuff[mZzBuffIndex] = oneByte
                        mZzBuffIndex++
                        //新数据已加入缓存，从缓存中检验是否有完整数据
                        xsVerificationData()
                    }

                } catch (e: java.lang.Exception) {
                    e.printStackTrace()
                }
            }
        }
    }

    //新数据已加入缓存，从缓存中检验是否有完整数据
    private fun xsVerificationData() {
        //System.out.println("BUFF：" +Arrays.toString(buff));
        if (mZzBuffIndex < 7) {
            //头+长度+data +校验+尾 就算数据为空也至少要4位
        } else {
            //帧头 第一个 0x4e
            if (mZzBuff[0] == mZzHead[0]) {
                //帧头 第二个 0x54
                if (mZzBuff[1] == mZzHead[1]) {
                    //数据长度
//                    val dataLengthByteArray = byteArrayOf(mZzBuff[2])
//                    val bytes2HexStr = ByteUtils.bytes2HexStr(dataLengthByteArray)
//                    val dataLength = 29
//                    d("xsVerificationData::dataLength:$dataLength")

                    if (mZzDataLength in 1..mZzDataMaxSize) {
                        if (mZzBuffIndex >= mZzDataLength) {
                            //没有结束符号，这里不做判断，但是可以判断检验码。
                            var crc = 0x00.toByte()
                            for (i in 12..mZzDataLength - 2) { //传重量是12个字节，第5条指令是17个字节，报文数据加起来都是29个字节，所以从13开始
                                crc = crc.xor(mZzBuff[i])
                            }
                            //校验CRC 这里有问题，crc不对
                            d("校验CRC=>计：${crc},它：${mZzBuff[mZzDataLength - 1]}")
                            if (crc == mZzBuff[mZzDataLength - 1]) {
                                //打印 CRC
                                //d("xsVerificationData::crc:$crc,mXSBuff[mZzDataLength-1]:${mZzBuff[mZzDataLength - 1]}")
                                mZzOutBuff = ByteArray(mZzDataLength)
                                System.arraycopy(mZzBuff, 0, mZzOutBuff, 0, mZzDataLength)
                                //use outBuff
                                mZzOutBuff?.let { handRealDataXS(it) }

                                //清除已使用数据
                                System.arraycopy(mZzBuff, mZzDataLength, mZzBuff, 0, mZzDataLength)
                                for (i in 0 until mZzDataLength) {
                                    mZzBuff[mZzBuffIndex - mZzDataLength + i] = 0X00
                                }
                                mZzBuffIndex -= mZzDataLength
                                //再次检验
                                xsVerificationData()
                            } else {
                                System.arraycopy(mZzBuff, 1, mZzBuff, 0, mZzBuffIndex - 1)
                                mZzBuff[mZzBuffIndex - 1] = 0X00
                                mZzBuffIndex = mZzBuffIndex - 1
                                //再次检验
                                xsVerificationData()
                            }

                        } else {
                            //已缓存的数据还没有这没多，继续等待缓存
                            //本方法可能存在干扰数据正好与hed相等，然后第二位是干扰数据或者是真的数据hed,此时把第二位当数据长度会出现问题
                            //比如缓存中有数据3B 28 3B 09 ..... 0A，其中3B 28为干扰数据，后面还有个完整的数据包，
                            //那么此方法会等到再有数据进来，缓存中有0X28个数据时才会判断出3B 28为无效数据，就造成了延时问题，
                            //或者后面不会再有数据进来，那么后面那个真的完整数据就会存在缓存中而没有拿出来使用。
                            //解决办法：
                            //一、增加hed头为多个字节，减少干扰和hed的相等的几率。
                            //二、在此继续向后面检查是否有完整数据，但是检测前不清除前面的buff,如果有的话再清空buff中的使用数据和前面的无效数据
                        }
                    } else {
                        //数据长度超过规定长度，肯定是错误数据，舍弃1位
                        System.arraycopy(mZzBuff, 1, mZzBuff, 0, mZzBuffIndex - 1)
                        mZzBuff[mZzBuffIndex - 1] = 0X00
                        mZzBuffIndex = mZzBuffIndex - 1
                        //再次检验
                        xsVerificationData()
                    }
                } else {
                    //第二个数据不是hed,舍弃
                    System.arraycopy(mZzBuff, 2, mZzBuff, 0, mZzBuffIndex - 2)
                    mZzBuff[mZzBuffIndex - 1] = 0X00
                    mZzBuff[mZzBuffIndex - 2] = 0X00
                    mZzBuffIndex = mZzBuffIndex - 2
                    //再次检验
                    xsVerificationData()
                }

            } else {
                //第一个数据不是hed,舍弃
                System.arraycopy(mZzBuff, 1, mZzBuff, 0, mZzBuffIndex - 1)
                mZzBuff[mZzBuffIndex - 1] = 0X00
                mZzBuffIndex = mZzBuffIndex - 1
                //再次检验
                xsVerificationData()
            }
        }
    }

    /*定义下面数据来处理稳定状态*/
    private var mAddUpStableTimes = 0
    private var mLastWeightDouble = 0.0
    private var mMaxAddUpStableTimes = 5
    private var mStableWeightDouble = 0.0//稳定后的重量

    /*处理分析zz数据*/
    fun handRealDataXS(data: ByteArray) {
        //共29字节，前12字节跟重量有关，后面17字节跟防作弊有关
        var weightStr = arrayOf(data[3], data[4], data[5], data[6], data[7], data[8], data[9]).toByteArray().decodeToString().trim()
        var weightDou = weightStr.toDoubleWithTwoDecimalPlaces()


        //负数,0X2B是正数
        if (0X2D.toByte() == data[2]) {
            weightDou = -weightDou
            weightStr = "-".plus(weightStr)
        }
        var weightBean = WeightBean().apply {
            var weight = weightDou - mTempWeight
            this.weightString = String.format("%.2f", weight)
            this.weightDouble = weight
            //处理是否稳定
            when {
                weightDou != mLastWeightDouble -> {
                    mAddUpStableTimes = 0
                    this.isStable = false
                }
                mAddUpStableTimes >= mMaxAddUpStableTimes -> {
                    mAddUpStableTimes = mMaxAddUpStableTimes
                    this.isStable = true
                }
                else -> {
                    mAddUpStableTimes += 1
                }
            }
            mIsStable = this.isStable
            if (mIsStable) mStableWeightDouble = weightDou
            mLastWeightDouble = weightDou
        }
        EventBusUtil.sendEvent(Event(EventCode.WEIGHT_DATA_ALL, weightBean))
        d("处理后：${ByteUtils.bytes2HexStr(data)},weight:${weightBean.weightString}")
    }

    fun d(msg: String) {
        //CommUtils.println("SerialPortSuperWinUtils->", msg)
    }

    fun setZero() {
        if (mSerialPort == null) return
        if (mSerialPort!!.outputStream == null) return
        mSerialPort!!.outputStream.write(ByteUtils.hexStr2Bytes("020001C4C7"))
        mTempWeight = 0.0
    }

    /*去皮*/
    fun setPeel() {
//        if (mSerialPort == null) return
//        if (mSerialPort!!.outputStream == null) return
//        //mSerialPort!!.outputStream.write(WeightConstants.QP_CMD)
//        mSerialPort!!.outputStream.write(ByteUtils.hexStr2Bytes("020001C4C7"))
        if (mIsStable) {
            mTempWeight += (mStableWeightDouble - mTempWeight)
        } else {
            ToastUtil.showToast("状态不稳定!")
        }
    }

    /*清皮*/
    fun clearPeel() {
//        if (mSerialPort == null) return
//        if (mSerialPort!!.outputStream == null) return
//        //mSerialPort!!.outputStream.write(WeightConstants.QP_CMD)
//        mSerialPort!!.outputStream.write(ByteUtils.hexStr2Bytes("020001C4C7"))
        if (mIsStable) {
            mTempWeight = 0.0
        } else {
            ToastUtil.showToast("状态不稳定!")
        }
    }

    fun setAdjustingScale() {
        if (mSerialPort == null) return
        if (mSerialPort!!.outputStream == null) return
        mSerialPort!!.outputStream.write(ByteUtils.hexStr2Bytes(""))
    }
}